/**
 * 文件更新接口测试用例
 *
 * Created by snowingsea on 2020/02/27.
 */
const download = require('download');
const fs = require('fs-extra');
const gitUrlParse = require('git-url-parse');
const path = require('path');
const randomstring = require('randomstring');
const SimpleGit = require('simple-git/promise');

const chai = require('chai');
const should = chai.should(); // eslint-disable-line no-unused-vars
chai.use(require('chai-http'));

const common = require('./../../common');
const Administrator = require('./../../../models/administrator');
const File = require('./../../../models/file');
const uri = require('./../uri');
const config = require('./../../../api/api-config');

const files = require('./../../../../phoenix/models/files');
const utility = require('./../../../../phoenix/models/utility');

const remotePackageURL = 'https://nchc.dl.sourceforge.net/project/cjson/cJSONFiles.zip';

module.exports = describe('/PUT files/:id/file.json', function () {
  const administrator = new Administrator();
  let rootDirectory;
  const newUserIds = [];
  function url(fileId) {
    if (!fileId) {
      fileId = rootDirectory.fileId;
    }
    return `/files/${fileId}/file.json`;
  }

  before(function (done) {
    administrator.login()
      .then(() => administrator.createRandomTester())
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((project) => project.createRandomVersion())
      .then((res) => {
        rootDirectory = res.rootDirectory;
        done();
      })
      .catch(done);
  });
  after(function (done) {
    administrator.removeUsers(newUserIds)
      .then(() => {
        done();
      })
      .catch(done);
  });

  common.should.be.user(uri, url, 'put');
  common.should.be.tester(uri, url, 'put');

  // 压缩包更新
  it('it should UPDATE files with package', function (done) {
    let homeDir;
    let uploadPath;
    File.createRandomPackage()
      .then((res) => {
        homeDir = res.homeDir;
        uploadPath = res.packagePath;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .put(url())
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file })
          .attach('file', fs.readFileSync(uploadPath), path.basename(uploadPath))
          .end((err, res) => {
            res.should.have.status(200);
            common.should.be.fileMessage(res.body);

            rootDirectory.getTree()
              .then((res) => {
                common.should.be.fileTreeMessage(res);
                res.tree.children = res.tree.children.filter((child) => {
                  const { kind } = child;
                  return kind === files.kind.directory;
                });
                if (res.tree.children.length === 0) {
                  return Promise.resolve();
                }
                res.tree.children.length.should.be.eql(1);
                res.tree.children[0].fileName.should.be.eql(path.basename(homeDir));
                return common.should.complete.directory(res.tree.children[0], homeDir);
              })
              .then(() => {
                done();
              })
              .catch(done);
          });
      })
      .catch(done);
  });

  // 设置远程文件更新
  it('it should update files with remote package file', function (done) {
    const filename = randomstring.generate(16);
    const dirPath = path.join(config.path.cacheDir, randomstring.generate(16));
    download(remotePackageURL, config.path.cacheDir, { filename })
      .then(() => utility.uncompress(path.join(config.path.cacheDir, filename), dirPath))
      .then(() => {
        chai.request(uri)
          .put(url())
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(200);
            common.should.be.fileMessage(res.body);

            rootDirectory.getTree()
              .then((res) => {
                common.should.be.fileTreeMessage(res);
                return common.should.complete.directory(res.tree, dirPath);
              })
              .then(() => {
                done();
              })
              .catch(done);
          });
      })
      .catch(done);
  });

  // 使用git更新文件
  [
    { url: 'http://10.88.222.102/phoenix-unittest/curl.git' },
    { url: 'http://10.88.222.102/phoenix-unittest/redis.git', branch: 'unstable' },
    {
      url: 'http://10.88.222.102/phoenix-unittest/logic_bombs.git',
      branch: 'stable',
      username: 'nongzhizhong',
      password: 'Nzz123456',
    },
  ].forEach((repo) => {
    it(`it should update files with git: ${JSON.stringify(repo)}`, function (done) {
      const localRepoPath = path.join(config.path.cacheDir, randomstring.generate(8));
      const { branch = 'master' } = repo;
      const gitInfo = gitUrlParse(repo.url);
      if (repo.password) {
        gitInfo.user = `${repo.username}:${repo.password}`;
      }
      SimpleGit()
        .silent(true)
        .clone(gitUrlParse.stringify(gitInfo), localRepoPath, ['-b', branch, '--depth=1'])
        .then(() => {
          const query = {
            type: files.repositoryType.git,
            url: repo.url,
            branch: repo.branch,
            username: repo.username,
          };
          chai.request(uri)
            .put(url())
            .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
            .query(query)
            .send({ password: repo.password })
            .end((err, res) => {
              res.should.have.status(200);
              common.should.be.fileMessage(res.body);
              res.body.should.have.property('repository');
              res.body.repository.should.have.property('type');
              res.body.repository.type.should.be.eql(files.repositoryType.git);
              res.body.repository.should.have.property('url');
              // res.body.repository.url.should.be.eql(repo.url);
              res.body.repository.should.have.property('branch');
              res.body.repository.branch.should.be.eql(branch);
              if (res.body.repository.username) {
                res.body.repository.username.should.be.eql(repo.username);
              }

              rootDirectory.getTree()
                .then((res) => {
                  common.should.be.fileTreeMessage(res);
                  return common.should.complete.directory(res.tree, localRepoPath);
                })
                .then(() => {
                  done();
                })
                .catch(done);
            });
        })
        .catch(done);
    });
  });

  // 不可更新冻结项目的文件
  it('it should fail to update file in frozen project', function (done) {
    let newProjectId;
    let newDirectoryId;
    rootDirectory.projectVersion.project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then(({ rootDirectory }) => {
        newDirectoryId = rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.freezeProject(newProjectId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .put(url(newDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可更新冻结项目版本的文件
  it('it should fail to update a file in frozen project version', function (done) {
    let newVersionId;
    let newDirectoryId;
    rootDirectory.projectVersion.project.createRandomVersion()
      .then(({ versionId, rootDirectory }) => {
        newVersionId = versionId;
        newDirectoryId = rootDirectory.fileId;
        return rootDirectory.projectVersion.project.freezeVersion(newVersionId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .put(url(newDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可更新他人项目的文件
  it('it should fail to update a file when the project is not yours', function (done) {
    let otherDirectoryId;
    administrator.createRandomTester()
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((project) => project.createRandomVersion())
      .then((res) => {
        otherDirectoryId = res.rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .put(url(otherDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可更新不存在项目的文件
  it('it should fail to update a file when the project is not exist', function (done) {
    let newProjectId;
    let newDirectoryId;
    rootDirectory.projectVersion.project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then((res) => {
        newDirectoryId = res.rootDirectory.fileId;
        return rootDirectory.projectVersion.project.creator.removeProject(newProjectId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .put(url(newDirectoryId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可更新不存在的文件
  it('it should fail to update a file when the file is not exist', function (done) {
    let newFileId;
    rootDirectory.createEmptyFile()
      .then((file) => {
        newFileId = file.fileId;
        return rootDirectory.removeFile(newFileId);
      })
      .then(() => rootDirectory.projectVersion.project.creator.login())
      .then(() => {
        chai.request(uri)
          .put(url(newFileId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.file, url: remotePackageURL })
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可向文件使用git更新内容
  it('it should fail to update a file with git when the file is file', function (done) {
    let newFileId;
    rootDirectory.createEmptyFile()
      .then(({ fileId }) => {
        newFileId = fileId;
        return rootDirectory.projectVersion.project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .put(url(newFileId))
          .set('Authorization', `Bearer ${rootDirectory.projectVersion.project.creator.token.accessToken}`)
          .query({ type: files.repositoryType.git, url: 'http://10.88.222.102/phoenix-unittest/curl.git' })
          .end((err, res) => {
            res.should.have.status(416);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });
});
